package com.chenfan.mcn.controller;


import com.alibaba.excel.EasyExcel;
import com.alibaba.excel.context.AnalysisContext;
import com.alibaba.excel.event.AnalysisEventListener;
import com.alibaba.excel.metadata.CellExtra;
import com.alibaba.excel.metadata.data.ReadCellData;
import com.alibaba.excel.read.listener.ReadListener;
import com.alibaba.excel.support.ExcelTypeEnum;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.chenfan.infra.utils.StringUtils;
import com.chenfan.mcn.annotations.ActionTracing;
import com.chenfan.mcn.config.UserVoContextHolder;
import com.chenfan.mcn.enums.ApprovalStatusEnum;
import com.chenfan.mcn.enums.OperateLockEnum;
import com.chenfan.mcn.exception.McnErrorCode;
import com.chenfan.mcn.extension.Res;
import com.chenfan.mcn.extension.excel.DynamicExcel;
import com.chenfan.mcn.extension.excel.ExcelUtils;
import com.chenfan.common.extension.validation.groups.Modify;
import com.chenfan.common.extension.lock.DistributedLockTemplate;
import com.chenfan.mcn.model.McnPage;
import com.chenfan.mcn.model.StarRateCard;
import com.chenfan.mcn.model.dto.*;
import com.chenfan.mcn.model.vo.*;
import com.chenfan.mcn.service.StarBasicDataService;
import com.chenfan.mcn.service.StarRateCardService;
import com.chenfan.mcn.utils.BusinessAssert;
import com.chenfan.mcn.utils.CommonUtil;
import com.chenfan.mcn.utils.ValidatorUtil;
import com.chenfan.privilege.common.config.SearchAuthority;
import com.chenfan.privilege.common.config.UrlAuth;
import com.google.common.collect.Lists;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.collections4.MapUtils;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.validation.annotation.Validated;
import org.springframework.web.bind.annotation.*;
import org.springframework.web.multipart.MultipartFile;

import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.math.BigDecimal;
import java.time.LocalDate;
import java.time.ZoneId;
import java.util.*;
import java.util.stream.Collectors;

/**
 * 红人刊例管理
 * @author wulg
 * @since 2021-12-29
 */
@Slf4j
@RestController
@RequestMapping("/starRateCard")
public class StarRateCardController {

    @Autowired
    private StarRateCardService starRateCardService;

    @Autowired
    private StarBasicDataService starBasicDataService;


    /**
     * 新增刊例
     * @param dto
     * @return
     */
    @PostMapping("/add")
    @UrlAuth({"advertisementManage_add"})
    public Res<Long> add(@Validated  @RequestBody StarRateCardSaveDTO dto) {
        return Res.ok(starRateCardService.saveStarRateCard(dto));
    }

    /**
     * 编辑刊例名称
     * @param dto
     * @return
     */
    @PostMapping("/modify")
    @UrlAuth({"advertisementManage_add"})
    public Res<Long> modify(@Validated({Modify.class}) @RequestBody StarRateCardSaveDTO dto) {
        return DistributedLockTemplate.execute(OperateLockEnum.STAR_RATE_CARD.generateKey(dto.getId()),
                () -> Res.ok(starRateCardService.saveStarRateCard(dto)));
    }

    /**
     * 刊例明细分页查询信息
     * @param queryDTO
     * @return
     */
    @PostMapping("/queryDetails")
    public Res<StarRateCardDetailQueryVO> queryDetails(@Validated @RequestBody StarRateCardDetailQueryDTO queryDTO) {
        StarRateCard starRateCard = starRateCardService.getStarRateCard(queryDTO.getId(), true);
        return Res.ok(starRateCardService.queryDetails(starRateCard, queryDTO));
    }

    /**
     * 新增刊例详情
     * @param dto
     * @return
     */
    @PostMapping("/addDetail")
    public Res<Boolean> addDetail(@RequestBody StarRateCardDetailFillInDTO dto) {
        BusinessAssert.notNull(dto.getCardId(), McnErrorCode.PARAM_ERROR, "刊例ID不能为空");
        BusinessAssert.notEmpty(dto.getStarIds(), McnErrorCode.PARAM_ERROR, "请选择要添加的红人");
        return DistributedLockTemplate.execute(OperateLockEnum.STAR_RATE_CARD.generateKey(dto.getCardId()),
                () -> Res.ok(starRateCardService.saveAndFillInDetail(dto)));
    }

    /**
     * 编辑刊例详情
     * @param dto
     * @return
     */
    @PostMapping("/modifyDetail")
    public Res<Boolean> modifyDetail(@RequestBody StarRateCardDetailSaveDTO dto) {
        BusinessAssert.notNull(dto.getCardId(), McnErrorCode.PARAM_ERROR, "刊例ID不能为空");
        BusinessAssert.notNull(dto.getConfirmStatus(), McnErrorCode.PARAM_ERROR, "确认状态不能为空");
        BusinessAssert.notEmpty(dto.getItems(), McnErrorCode.PARAM_ERROR, "编辑无内容");
        dto.getItems().forEach(item -> {
            ValidatorUtil.validate(dto.getItems(), Modify.class);
        });
        return DistributedLockTemplate.execute(OperateLockEnum.STAR_RATE_CARD.generateKey(dto.getCardId()),
                () -> Res.ok(starRateCardService.saveAndConfirmDetail(dto)));
    }

    /**
     * 刊例分页查询
     * @param searchDTO
     * @return
     */
    @PostMapping("/page")
    @UrlAuth({"advertisementManage_search"})
    public Res<Page<StarRateCardListVO>> page(@SearchAuthority(filterCustomer = false) StarRateCardSearchDTO searchDTO) {
        return Res.ok(starRateCardService.queryByPage(searchDTO));
    }

    /**
     * 刊例详情
     * @param id
     * @return
     */
    @GetMapping("/detail")
    public Res<StarRateCardInfoVO> detail(@RequestParam Long id) {
        BusinessAssert.notNull(id, McnErrorCode.PARAM_ERROR, "刊例ID不能为空");
        return Res.ok(starRateCardService.detail(id));
    }

    /**
     * 修改单据状态
     *
     * @param id {@link Long}
     * @param status   {@link Integer}
     * @return {@link Boolean}
     */
    @GetMapping(value = "/changeStatus")
    @UrlAuth({"advertisementManage_submit","advertisementManage_resubmit","advertisementManage_invalid","advertisementManage_revocation"})
    public Res<Boolean> changeStatus(@RequestParam Long id, @RequestParam Integer status) {
        BusinessAssert.notNull(id, McnErrorCode.PARAM_ERROR, "刊例ID不能为空");
        BusinessAssert.notNull(status,McnErrorCode.PARAM_ERROR, "刊例状态不能为空");
        BusinessAssert.notNull(ApprovalStatusEnum.anyMatch(status, ApprovalStatusEnum.APPROVAL, ApprovalStatusEnum.CANCEL, ApprovalStatusEnum.INVALID),
                McnErrorCode.PARAM_ERROR, "刊例状态标记错误！");
        return DistributedLockTemplate.execute(OperateLockEnum.STAR_RATE_CARD.generateKey(id),
                () -> Res.ok(starRateCardService.changeStatus(id, status)));
    }

    /**
     * 确认更新操作
     * @param dto
     * @return
     */
    @PostMapping(value = "/confirmUpdate")
    @UrlAuth({"advertisementManage_confirmUpdate"})
    public Res<Boolean> confirmUpdate(@Validated @RequestBody StarRateCardConfirmUpdateDTO dto) {
        return DistributedLockTemplate.execute(OperateLockEnum.STAR_RATE_CARD.generateKey(dto.getId()),
                () -> Res.ok(starRateCardService.confirmUpdate(dto)));
    }

    /**
     * 复制历史刊例
     * @param id
     * @return
     */
    @GetMapping(value = "/copyHistory")
    @UrlAuth({"advertisementManage_copyHistory"})
    public Res<Boolean> copyHistory(@RequestParam Long id) {
        BusinessAssert.notNull(id, McnErrorCode.PARAM_ERROR, "刊例ID不能为空");
        return DistributedLockTemplate.execute(OperateLockEnum.STAR_RATE_CARD.generateKey(id),
                () -> Res.ok(starRateCardService.copyHistory(id)));
    }

    /**
     * （批量）删除明细
     * @param dto
     * @return
     */
    @PostMapping(value = "/deleteDetails")
    public Res<Boolean> deleteDetails(@Validated @RequestBody StarRateCardDetailDeleteDTO dto) {
        return DistributedLockTemplate.execute(OperateLockEnum.STAR_RATE_CARD.generateKey(dto.getId()),
                () -> Res.ok(starRateCardService.deleteDetails(dto)));
    }

    /**
     * 获取刊例关联的平台Tab栏信息
     * @param id
     * @return
     */
    @GetMapping(value = "/getPlatformTab")
    public Res<List<StarRateCardPlatformTabVO>> getPlatformTab(@RequestParam Long id) {
        BusinessAssert.notNull(id, McnErrorCode.PARAM_ERROR, "刊例ID不能为空");
        return Res.ok(starRateCardService.getPlatformTab(id));
    }

    /**
     * 导出刊例
     * @param exportDTO
     * @param response
     */
    @PostMapping("/export")
    public void exportExcel(@Validated @RequestBody StarRateCardExportDTO exportDTO, HttpServletResponse response) {
        BusinessAssert.notNull(exportDTO.getId(), McnErrorCode.PARAM_ERROR, "刊例ID不能为空");
        try {
            StarRateCard starRateCard = starRateCardService.getStarRateCard(exportDTO.getId(), true);
            List<DynamicExcel> dynamicExcelList = starRateCardService.getDynamicExcelList(starRateCard);
            ExcelUtils.exportDynamicExcel(response, starRateCard.getCardName()+".xlsx", dynamicExcelList);
        } catch (Exception e) {
            log.error(e.getMessage(), e);
        }
    }

    /**
     * 添加红人-分页查询
     * @param queryDTO
     * @return
     */
    @PostMapping("/getStarList")
    public Res<Page<StarBasicDataVO>> getStarList(@Validated @RequestBody RateCardStarQueryDTO queryDTO) {
        List<Long> excludeStarIds = starRateCardService.getAllStarId(queryDTO.getCardId());
        StarBasicDataSearchDTO searchDTO = new StarBasicDataSearchDTO();
        BeanUtils.copyProperties(queryDTO, searchDTO);
        McnPage<StarBasicDataVO> page = new McnPage<>();
        page.setPageNum(queryDTO.getPageNum());
        page.setPageSize(queryDTO.getPageSize());
        searchDTO.setTenantId(UserVoContextHolder.getTenantId());
        searchDTO.setStarType(queryDTO.getStarType());
        searchDTO.setStarNickname(queryDTO.getStarNickname());
        searchDTO.setStarAgentId(queryDTO.getStarAgentId());
        searchDTO.setPlatNickName(queryDTO.getPlatNickName());
        searchDTO.setMainActivePlatforms(queryDTO.getMainActivePlatforms());
        searchDTO.setExcludeStarIds(excludeStarIds);
        return Res.ok(starBasicDataService.getList(page, searchDTO));
    }

    /**
     * 添加红人平台-分页查询
     * @param queryDTO
     * @return
     */
    @PostMapping("/getStarPlatformList")
    public Res<Page<RateCardStarPlatformVO>> getStarPlatformList(@Validated @RequestBody RateCardStarPlatformQueryDTO queryDTO) {
        return Res.ok(starRateCardService.getStarPlatformList(queryDTO));
    }


    /**
     * 添加红人平台
     * @param dto
     * @return
     */
    @PostMapping("/addStarPlatform")
    public Res<Boolean> addStarPlatform(@Validated @RequestBody StarRateCardDetailAddPlatformDTO dto) {
        return DistributedLockTemplate.execute(OperateLockEnum.STAR_RATE_CARD.generateKey(dto.getCardId()),
                () -> Res.ok(starRateCardService.addStarPlatform(dto)));
    }

    /**
     * 催办经纪人
     * @param dto
     * @return
     */
    @PostMapping(value = "/remindAgent")
    @UrlAuth({"advertisementManage_urgeTheBrokerUpdate"})
    public Res<Boolean> remindAgent(@Validated @RequestBody StarRateCardRemindDTO dto) {
        return DistributedLockTemplate.execute(OperateLockEnum.STAR_RATE_CARD.generateKey(dto.getId()),
                () -> Res.ok(starRateCardService.remindAgent(dto)));
    }

    /**
     * 获取刊例报价
     * @return
     */
    @PostMapping(value = "/getRateCardPrice")
    public Res<BigDecimal> getRateCardPrice(@Validated @RequestBody RateCardPriceQueryDTO dto) {
        if(Objects.isNull(dto.getPublishDate())) {
            dto.setPublishDate(Date.from(LocalDate.now().atStartOfDay().atZone(ZoneId.systemDefault()).toInstant()));
        }
        return Res.ok(starRateCardService.getRateCardPrice(dto));
    }


    /**
     * 导入刊例
     * @param id 刊例ID
     * @param multipartFile
     * @return
     */
    @PostMapping("/importExcel")
    public Res<String> importExcelById(@RequestParam MultipartFile multipartFile, @RequestParam Long id) throws IOException {
        BusinessAssert.isTrue(StringUtils.equalsAnyIgnoreCase(CommonUtil.getFileNameSubfix(multipartFile.getOriginalFilename()),
                ExcelTypeEnum.XLSX.getValue(), ExcelTypeEnum.XLS.getValue()), McnErrorCode.PARAM_ERROR, "文档格式不正确！");
        StarRateCard starRateCard = starRateCardService.getStarRateCard(id, true);
        BusinessAssert.isTrue(ApprovalStatusEnum.anyMatch(starRateCard.getStatus(),
                        ApprovalStatusEnum.UN_COMMIT, ApprovalStatusEnum.CANCEL, ApprovalStatusEnum.REJECTED), "当前刊例状态-不允许导入！");
        Map<String, Map<Integer, String>> sheetHeaderMap = new LinkedHashMap<>(8);

        Map<String, List<Map<Integer, String>>> sheetBodyMap = new LinkedHashMap<>(8);
        EasyExcel.read(multipartFile.getInputStream(), new AnalysisEventListener<Map<Integer, String>>() {

            @Override
            public void onException(Exception exception, AnalysisContext context) throws Exception {
                log.error(exception.getMessage(), exception);
            }

            @Override
            public void invokeHeadMap(Map<Integer, String> headMap, AnalysisContext context) {
                // 获取当前sheet的名称
                String sheetName = context.readSheetHolder().getSheetName();
                sheetHeaderMap.putIfAbsent(sheetName, headMap);
            }

            @Override
            public void invoke(Map<Integer, String> rowData, AnalysisContext context) {
                // 获取当前sheet的名称
                String sheetName = context.readSheetHolder().getSheetName();
                if(!sheetBodyMap.containsKey(sheetName)) {
                    List<Map<Integer, String>> dataList = new ArrayList<>();
                    dataList.add(rowData);
                    sheetBodyMap.putIfAbsent(sheetName, dataList);
                } else {
                    sheetBodyMap.get(sheetName).add(rowData);
                }
            }

            @Override
            public void doAfterAllAnalysed(AnalysisContext context) {

            }
        }).doReadAll();
        BusinessAssert.notEmpty(sheetBodyMap, McnErrorCode.PARAM_ERROR, "文件不能为空！");
        //如果导入失败，返回失败文件ID
        String fileId = starRateCardService.importExcelById(starRateCard, sheetHeaderMap, sheetBodyMap);
        return Res.ok(fileId);
    }

}
